import React, { memo, useCallback, forwardRef, useImperativeHandle, useEffect, useRef, useState, useMemo } from 'react';
import { handleFormItemsParamStr, handleInitialValues, getExtraData, handleValues, handleBindRelations, getDisabled, getRules, diffFormItems, EXTRA_SESSION_KEY } from './util';
import { FormxProps, FormItemProps, FormxRefProps } from './interface';
import { Form, Row, Skeleton } from 'antd';
import classnames from 'classnames';
import styles from './formx.module.scss';
import ControlItem from './ControlItem';

const gutter = [24, 0];

const Formx = (props: FormxProps, ref: React.Ref<FormxRefProps>) => {

  const { formItems, groupNumber, initialValues: formInitialValues, onValuesChange, noMergeInitialValues, ...formProps } = props;
  const extraData = useRef({});
  const [form] = Form.useForm();
  const [handleFormItems, setHandleFormItems] = useState<FormItemProps[]>([]);
  const [loading, setLoading] = useState(false);

  useImperativeHandle(ref, () => ({
    ...form,
    validateFields: async () => {
      const values = await form.validateFields();
      const extrData = sessionStorage[EXTRA_SESSION_KEY] ? JSON.parse(sessionStorage[EXTRA_SESSION_KEY]) : {};
      return { ...extraData.current, ...extrData, ...handleValues(formItems, values) };
    }
  }));

  useEffect(() => {
    return () => sessionStorage.removeItem(EXTRA_SESSION_KEY);
  }, []);

  useEffect(() => {
    if (formItems.length === 0) return setLoading(true);
    if (formItems.length > 0) return setLoading(false);
  }, [formItems]);

  const initialValues = useMemo(() => {
    const initialValuesInFormItems = formItems.reduce((next, cur) => {
      next[cur.name] = cur.options?.initialValue;
      return next;
    }, {});
    return { ...initialValuesInFormItems, ...formInitialValues };
  }, [formItems, formInitialValues]);

  useEffect(() => {
    if (initialValues && formItems.length) {
      form.setFieldsValue(handleInitialValues(initialValues, formItems));
      if (!noMergeInitialValues) extraData.current = initialValues;
    }
  }, [initialValues, form, formItems, noMergeInitialValues]);

  useEffect(() => {
    setHandleFormItems(handleFormItemsParamStr(formItems, initialValues || {}));
  }, [formItems, groupNumber, initialValues]);

  const _onValuesChange = useCallback((changedValues, allValues) => {
    const formItemExtraData = getExtraData(formItems, changedValues);
    extraData.current = { ...extraData.current, ...allValues, ...formItemExtraData };

    const fieldsValue = handleBindRelations(
      formItems,
      { ...changedValues, ...formItemExtraData },
      extraData.current
    );

    const newFormItemsGroup = handleFormItemsParamStr(formItems, extraData.current);
    diffFormItems(handleFormItems, newFormItemsGroup, fieldsValue, extraData.current);

    setHandleFormItems(newFormItemsGroup);

    // 可在onValuesChange里返回对象，用于设定form控件的值
    const handleAfterValues = onValuesChange?.(changedValues, extraData.current);

    const newFieldsValue = { ...formItemExtraData, ...fieldsValue };
    delete newFieldsValue[Object.keys(changedValues)[0]];

    form.setFieldsValue({ ...newFieldsValue, ...handleAfterValues });
  }, [formItems, onValuesChange, form, groupNumber, handleFormItems]);

  return (
    <Skeleton active loading={loading}>
      <div className={classnames({
        [styles.formx]: true,
        [styles[localStorage.i18nextLng]]: (!formProps.layout || formProps.layout === 'horizontal') && !formProps.noLangFormUi
      })}>
        <Form
          layout='horizontal'
          {...formProps}
          initialValues={initialValues}
          onValuesChange={_onValuesChange}
          form={form}>
          <Row gutter={gutter as any}>
            {handleFormItems.map(item => (
              <ControlItem
                id={item.name}
                key={item.name}
                extra={props.extra}
                extraInitialValue={extraData.current['_' + item.name]}
                disabled={props.disabled || getDisabled(item, extraData.current)}
                rules={getRules(item, extraData.current)}
                groupNumber={groupNumber}
                formItem={item} />
            ))}
            {props.children}
          </Row>
        </Form>
      </div>
    </Skeleton>
  );
};

export default memo(forwardRef(Formx));